"""
Python Make System.

This script is used to run the PyMak scripts in a component. All
a component needs is a bat or shell file in its top level folder which
does the following:

@echo off
python -u %PATH_TO_PYMAK%\pm.py %*

on Windows, or

#!/bin/sh
python -u $(PATH_TO_PYMAK)/pm.py $*

for Unix/Linux.


The main features of this make system are:

1) Platform independent
2) Supports multi-varianted builds from the ground up.
3) Reduces the amount of repeated dross in numerous sub-makefiles.
4) Eliminates the need for the make to delegate to other shell contexts, such
   as Perl and Cygwin (a notoriously problematic technique).

Why a another make system? Using a make system like GNUmake in can be
quite awkward when trying to make it build a varianted product. In
order to set up the varianted directories GNUmake requires complex
pattern expressions. Furthermore the construction of a deep object
tree is almost impossible.

The heart of the system is based around a number of simple classes:
Variants, VariantValues, Target, Rules and Actions. The make system
is designed to read in all the sub makefiles first and then build all
the targets in one go - there is no recursive invocation of the make
system. A tree of Target objects is built up. As each Target is
introduced to the tree it must have a list of Variants that that
target can vary by. A Rule is used as a factory that create Targets
for particular needs and inserts them into the tree. A Rule is chosen
based on the extension of the file that forms the Target. To make the
targets, a list of VariantValues is needed to bind the variants of
each of the targets encountered in the tree. In order to make a
Target, an Action is invoked. The Action is usually selected by the
Rule that created the Target.

The property table is used to hold variables that are variant
specific. The best way of seeing how this table works is to dump one
of the entries in the top level dictionary using the
dump_property_table_key function.

The Python logging module is crucial to PyMak. Most pymak files, and
pymak extensions should stick to the following convention.

 - Use the root logger for all logging.
 - Use logging.error for flagging a fatal error. Normally an exception should be raised
   in these circumstances. Alternatively, just raising a pymak.PyMakException with the
   reason is sufficient for it to be printed by the exception handling code in this
   pymak wrapper.
 - Use logging.warning for terse output.
 - Use logging.info for a bit more verbose output.
 - Use logging.debug for dump lots of data.

 If generating the logging output is going to be time-consuming, then use the following
 pattern to reduce the amount of wasted computation:

     if logging.getLogger().getEffectiveLevel() <= logging.DEBUG:
         logging.debug(...)

The Python files are extensively commented, and CHM files are generated
from them to make looking at this documentation easier. Useful starting
points are:

 - pm: comments in this module
 - context: the 'container' object. Look at the 'debug' method documentation.
 - target, rule and action - the holy trinity of PyMak.

"""

# Python imports
import os
import sys
import logging
import gettext

if os.path.isdir('pymak'):
    # use the local pymak rather than the installed one
    sys.path.insert(0, '.')
    

# PyMak imports
import pymak
import pymak.builtin


# this installs a _() method into the builtins using the gettext messages that
# are defined for the current locale.
gettext.translation('pymak', os.path.join(os.getcwd(), 'resources'), fallback=True).install()


# Make a default context which practically everyone can use.
pmc = pymak.PyMakContext()

# Consume the args that pymak understands
if not pmc.parse_args():
    # It should have reported why.
    sys.exit(1)

# The 'builtin' extension is always there
pymak.builtin.init(pmc)

# Make sure the current directory is in the path because many pmk files
# import helper python modules which are in the same directory as
# the pmk file itself.
sys.path = ['.'] + sys.path

# see if we can load any .pmc files in the current directory. Note that definitions in the
# current scope that is available to the pmk files: Noteably:
#   pmc - the initial default context.
#   pymak - the module imported as 'pymak'
try:
    pmc.import_pmk_files()
except pymak.PyMakException, e:
    e.report()
    sys.exit(1)
except:
    logging.exception(_("Programmatic error while parsing pymak files"))
    sys.exit(1)

# Before building the world, let's show some debug if the
# logging is sufficiently enabled.
if logging.getLogger().getEffectiveLevel() <= logging.DEBUG:
    logging.debug(_("Properties table:"))
    pmc.all_properties.dump_property_table()

# Now we've built the tree of targets which should be hanging off the context,
# build those targets specified in the command line.
try:
    pmc.build_world()
except pymak.PyMakException, e:
    e.report()
    logging.info(_("Make failed"))
    sys.exit(1)
except:
    logging.exception(_("Programmatic error while building world"))
    logging.info(_("Make failed"))
    sys.exit(1)

# I guess everything went well...
logging.info(_("Make successful"))
sys.exit(0)
